6.2.1 Tuples
Mathematically, a
tuple
is an ordered list of elements.
In Myron, a tuple value is entered as a comma-separated list of
expressions delimited by parentheses. The displayed form is very
similar to the input form. For example, (1,3,5,7) represents both the
input syntax and the display of a tuple with 4 elements. However,
there is a minor variation in the input syntax of a single-element
tuple to distinguish it from a parenthesized subexpression. The
leading element of an entered tuple is allowed to be empty. Thus (6)
is a real subexpression while (,6) is a tuple with one element.
A tuple with 0 entries, called the empty
tuple, the 0-tuple or the null tuple, also uses the variation, being
entered as (,) and displayed as
(, ).
An element of a tuple may be any kind of expression. In particular it
may be another tuple element.
((0, 0), (1, 1), (2, 4), (3, 9))
is a tuple of tuples (but note it is not a matrix). The constituent
elements need not be the same length nor even the same type.
((, 0), (0, 1), (0, 1, 2))
is a valid tuple with entries of different lengths. (In preview of
§6.3, this tuple can be generated by
((x|x∈1, i)|i∈1, 3).)
An example of a tuple with both different element lengths and different collection
types is
(a, {b, c}, [(, d), (, e)]).
When all the elements of a tuple have the same type,
that type is called
the common type. If any element of a tuple
has a type different from any other element, the common type is said
to be indeterminate.
Tuples can be represented by variables.
A tuple variable is displayed as a simple variable in bold font, but
its input form has a different notation. To distinguish a tuple
variable from other variables, a tuple variable is decorated with the
type suffix t or ʈ [1].
A variable with a decoration like this is said to be explicitly typed.
A tuple can be bound to a variable by appearing in a definition in which
the variable appears on the defining side:
xʈ→((, 0), (0, 1), (0, 1, 2)). The variable does not need the ʈ decoration
in the input form
because the nature of the variable is inferred from the right side of
the definition. If, on the other hand, the variable on the defining
side does have a ʈ decoration, the elaboration must be a tuple expression.
As well, if the elaboration contains variables, a function will
be inferred for the left side.
Tuples can also be associated with variables by appearing in an
equation in which one side is a simple variable name. Like
definitions, the types on each side of an equation must
match. Thus a variable on
one side of an equation is promoted to a tuple variable if the other side of
the equation is a tuple expression. At binding time,
the association implied by the equation is promoted to a definition.
To illustrate these points, consider the difference between the input
forms
(1,2)=x,
x→(3,4),
yʈ→(3,4)+(5,6)
and
yʈ=0. In the first two expressions, x is inferred to be a tuple variable.
The third expression indicates explicitly that y is a tuple variable.
The fourth expression is erroneous because the two sides of the definition
do not balance with respect to type.
In situations where there is no context that can be used to infer
a variable's type,
the variable must be entered using the explicit form.
To illustrate, an expression with explicit tuple variables entered as
aʈ+bʈ
displays as
aʈ+bʈ.
In order for this expression to be
evaluated, definitions for a and b, like
aʈ→(1, 2, 3)
and
bʈ→(x|x∈1, 3)
must appear in the workspace.
Of course, incremental evaluation can always be performed by
selecting a variable and substituting. Contrast the tuple
expression above with an expression entered as
aʈ+b
and displayed as
aʈ+b. The latter case represents a mixed-type expression.
Binding for this expression would fail, as would an attempt to substitute,
because there is no definition for real b.
When interacting with unary and binary operators, a tuple has primary
precedence. Any binary operators that can be applied to scalars can be
applied to two tuples or to a tuple and a non-tuple. In the case of two tuples
with the same number of elements, the operation is
applied to corresponding elements of each tuple.
For
example,
the expression
(1, 2, 3)+(4, 5, 6)
simplifies to
(1+4, 2+5, 3+6). If the tuples do not
have the same number of elements, residual elements from the longer
tuple are appended to the result unchanged. This is called the residual rule.
For binary expressions with a scalar as one
operand and a tuple as the other, the scalar operand is treated as if it
represented a tuple with the same number of elements as the
tuple operand, with each element being a copy of the scalar value (the scalar rule).
The mixed-type expression
(1, 2, 3)+4
behaves like
(1, 2, 3)+(4, 4, 4)
(and, more concisely, like
(1, 2, 3)+(4|x∈1, #(1, 2, 3))) and simplifies to
(5, 6, 7).
The rules for mixed-type binary expressions also consider nested tuples.
If one operand is a tuple of tuple of scalars and the other is a tuple of scalars,
both tuples of scalars are boxed.
Then the rules become the same as for binary expressions with
scalar and tuple,
this time with operands of type box and tuple of box (the box rule). Thus
((0, 2), (3, -1))+(1, 1) transform to
((1, 3), (4, 0)).
The general case for mixed-type binary expressions follows
from the special cases described above.
If one operand is not a collection, the scalar rule applies.
If both operands are the same type of collection and one operand matches the common
type of the other,
the box rule applies;
if no such match can be made, the residual rule applies.
If the operands are collections with differing types,
the scalar rules applies with the operand of the "lesser" type taking the role of scalar.
The generalized rules are applied recursively until a situation
is reached in which the scalar rule can be applied.
Similarly, any unary operator that can be applied to scalars can be
applied to a tuple. For example,
-(1, 2, 3)
simplifies to
(, -1, -2, -3).
The cardinality of a tuple is produced by the unary operator #.
The operator
produces a real containing the number of elements in the tuple.
The input
#(1,(1,2),[(3,4),(5,6)])
displays as
#(1, (1, 2), [(3, 4), (5, 6)])
and simplifies to 3.
Cardinality cannot be distributed but here is a situation
where Restructure applies; after restructuring, the expression appears as
(, #1, #(1, 2), #[(3, 4), (5, 6)]) and
then simplifies to
((, 1), (, 2), (2, 2)).
The magnitude of a tuple is given by the bifix unary | operator.
|(a, b, c)|
simplifies to
√(a^2+b^2+c^2).
|(1, (1, 2), [(3, 4), (5, 6)])| is entered as |(1,(1,2),[(3,4),(5,6)])|.
Applied to the tuple, the operator evaluates the tuple's length as
√(1+(1, 2)^2+[(3, 4), (5, 6)]^2).
The magnitude operator cannot be distributed but the expression can be restructured to
(, |1|, |(1, 2)|, |[(3, 4), (5, 6)]|).
This now simplifies to
(1, √5, √86), with the first element being absolute value, the second
being vector length and the third being the sum of the squares of the elements of a matrix.
All, in a sense, mean magnitude.
A tuple of scalars can be treated as a position vector (see §9.7 for a
better way to deal with vectors). Adding or
subtracting two position vectors produces a position vector.
Tuples of any length can by combined into a larger tuple using the
concatenation operator.
(a, b)‖(d, e)
simplifies to
(a, b, c, d). The inverse occurs when the concatenation operator is introduced by
selecting and moving elements at either end of a tuple. That is,
(.{a}, b, c)
and ← produce
(, a)‖(b, c).
The horizontal concatenation operator combines tuples into a matrix by treating as rows.
(a, b, c)‖‖(d, e) simplifies to
[(a, b, c), (d, e, 0)].
The length of a vector represented by a tuple is denoted
|(a, b, c)|. This simplifies to
√(a^2+b^2+c^2).